#include <arpa/inet.h>    //字节序、地址转换头文件
uint32_t htonl(uint32_t hostint32);    //32位主机字节序转换成网络字节序，成功返回网络字节序的值
uint16_t htons(uint16_t hostint16);    //16位主机字节序转换成网络字节序，成功返回网络字节序的值
uint32_t ntohl(uint32_t netint32);    //32位网络字节序转换成主机字节序，成功返回主机字节序的值
uint16_t ntohs(uint16_t netint16);    //16位网络自己序转换成主机字节序，成功返回主机字节序的值
int inet_pton(int family,const char *strptr,void *addrptr);    //点分十进制数串转换成32位无符号整数，成功返回1
const char *inet_ntop(int family,const void *addrptr,char *strptr,size_t len);    //32位无符号整数转换成点分十进制数串，成功返回首地址，失败NULL
																  INET_ADDRSTRLEN 16
																  INET6_ADDRSTRLEN 16
#include <sys/socket.h>    //套接字头文件 
int socket(int family,int type,    int protocol);    //创建套接字，成功返套接字
		   AF_INET    SOCK_DGRAM   0
		   AF_INET6   SOCK_STREAM  IPPROTO_TCP
		   PF_PACKET  SOCK_RAW     IPPROTO_UDP
#include <netinet/in.h>    //地址结构头文件 
struct in_addr
{
	in_addr_t s_addr;    //4 byte
};
struct sockaddr_in    //ipv4套接字地址结构，定义时使用
{
	sa_family_t sin_family;    //2 byte
	in_port_t sin_port;    //2byte
	struct in_addr sin_addr;    //4 byte
	char sin_zero[8];    //8 byte
};
struct sockaddr    //通用套接字地址结，接口函数调用时使用
{
	sa_family_t sa_family;    //2 byte
	char sa_data[14];    //14 byte
};
//INADDR_ANY 本地通配地址，值为0
ssize_t sendto(int sockfd,const void *buf,size_t nbytes,int flags,const struct sockaddr *to,socklen_t addrlen);    //发送UDP数据至to指定的ip，成功返回发送字节数，失败返回-1
                                                        0
int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);    //绑定本地协议地址和sockfd，成功返回0
ssize_t recvfrom(int sockfd,void *buf,size_t nbytes,int flags,struct sockaddr *from,socklen_t *addrlen);    //接收UDP数据，保存至from指向的结构，成功返回接收到的字符数，失败-1
													0
//TFTP:简单文件传送协议，基于UDP，无用户有效认证，数据传输模式有二进制模式octet和文本模式netascii
//69号端口等待客户端请求，临时端口与客户端通信，每个数据包变化从1开始，需客户端ACK确认，超时重发
//TCP客户端：知道“服务器”的ip、port，主动连接
#include <sys/socket.h>    //TCP客户端头文件
int connect(int sockfd,const struct sockaddr *addr,socklen_t len);    //主动连接服务器，成功返回0
ssize_t send(int sockfd,const void *buf,size_t nbytes,int flags);    //发送数据，返回成功发送的字节数，不能发送0长度的数据包
ssize_t recv(int sockfd,void *buf,size_t nbytes,int flags);    //接受网络数据，返回成功接收的字节数
//TCP服务器具备确定地址，明确是服务器，等待连接
int listen(int sockfd,int backlog);    //套接字主动变被动，成功返回0
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);    //从已连接队列中取出一个，否则阻塞，返回已连接套接字
//关闭一个已连接套接字将导致另一端接收到一个0长度的数据包
//----------------------------------------------------------------------------
//多线程TCP服务器并发
#include <stdio.h>/*{{{*/
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>/*}}}*/

void *client_fun(void *arg)    //与客户端交互线程
{
	int connect_fd=(int)arg;    //客户端套接字

	close(connect_fd);
	return NULL;
}

int main(int argc,char *argv[])
{
	int sock_fd=socket(AF_INET,SOCK_STREAM,0);    //创建sockfd/*{{{*/
	if(0 > sock_fd)
	{
		perror("\nsocket>>>\n");
		return -1;
	}

	struct sockaddr_in own_addr,client_addr;    //服务器自身和客户端地址结构
	bzero(&own_addr,sizeof(own_addr));
	own_addr.sin_family=AF_INET;
	own_addr.sin_port=htons(8000);
	own_addr.sin_addr.s_addr=htonl(INADDR_ANY);

	socklen_t own_addr_len=sizeof(own_addr);    //地址结构长度
	socklen_t client_addr_len=sizeof(client_addr);

	if(0 != bind(sock_fd,(struct sockaddr *)&own_addr,own_addr_len))    //绑定
	{
		perror("\nbind>>>\n");
		close(sock_fd);
		return -2;
	}
	
	if(0 != listen(sock_fd,10))    //监听
	{
		perror("\nlisten>>>\n");
		close(sock_fd);
		return -3;
	}/*}}}*/

	while(1)
	{
		bzero(&client_addr,sizeof(client_addr));
		int connect_fd=accept(sock_fd,(struct sockaddr *)&client_addr,&client_addr_len);
		pthread_t tid;
		pthread_create(&tid,NULL,(void *)client_fun,(void *)connect_fd);
		pthread_detach(tid);
	}

	close(sock_fd);    //关闭监听套接字
	return 0;
}
//--------------------------------------------------------------------------------

